using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Text;
using Castle.Components.Validator;

namespace WineCellar.Web.Helpers
{
    /// <summary>
    /// SmartBinder is based on ValidatingBinder from http://code.google.com/p/sutekishop/
    /// </summary>
    public class SmartBinder
    {
        public static void UpdateFrom(object target, NameValueCollection values)
        {
            UpdateFrom(target, values, null);
        }

        public static void UpdateFrom(object target, NameValueCollection values, string objectPrefix)
        {

            var targetType = target.GetType();
            var typeName = targetType.Name;

            var exceptionMessage = new StringBuilder();

            foreach (var property in targetType.GetProperties())
            {
                if (property.Name.ToLower() != "id" && property.Name.ToLower() != "deleted")
                {
                    var propertyName = property.Name;
                    if (!string.IsNullOrEmpty(objectPrefix))
                    {
                        propertyName = objectPrefix + "." + property.Name;
                    }
                    if (values[propertyName] == null)
                    {
                        propertyName = typeName + "." + property.Name;
                    }
                    if (values[propertyName] == null)
                    {
                        propertyName = typeName + "_" + property.Name;
                    }
                    if (values[propertyName] != null)
                    {
                        var converter = TypeDescriptor.GetConverter(property.PropertyType);
                        var stringValue = values[propertyName];
                        if (!converter.CanConvertFrom(typeof(string)))
                        {
                            throw new FormatException("No type converter available for type: " + property.PropertyType);
                        }
                        try
                        {
                            object value = converter.ConvertFrom(stringValue);
                            property.SetValue(target, value, null);
                        }
                        catch (Exception exception)
                        {
                            if (exception.InnerException is FormatException ||
                                exception.InnerException is IndexOutOfRangeException)
                            {
                                exceptionMessage.AppendFormat("'{0}' is not a valid value for {1}<br />", stringValue, property.Name);
                            }
                            else if (exception.InnerException is ValidationException)
                            {
                                exceptionMessage.AppendFormat("{0}<br />", exception.InnerException.Message);
                            }
                            else
                            {
                                throw;
                            }
                        }
                    }
                    else
                    {
                        // boolean values like checkboxes don't appear unless checked, so set false by default
                        if (property.PropertyType == typeof(bool) && values[propertyName] != null)
                        {
                            property.SetValue(target, false, null);
                        }
                    }
                }

            }
            if (exceptionMessage.Length > 0)
            {
                throw new ValidationException(exceptionMessage.ToString());
            }
        }
    }
}